using System;
using System.Text;

/* 
 * XPlane_UDP_Message.cs
 * UDP Data in/out message from XPlane, only the "DATA " message has been implemented.
 */

namespace VEXREDUS_hil_interface
{
	public class XPlane_UDP_Message
	{
		#region Attributes
        // Raw byte values of message
		private byte[] rawData;
        public byte[] RawData { get { return rawData; } }
		
		// Message decomposition
		private string msgType;
		public string MsgType { get { return msgType != null ? msgType : ""; } }
		
		private int index;
		public int Index { get { return index; } }
		
		//private float[] vars = new float[8];
		private float[] vars = {0,0,0,0,0,0,0,0};
		public float[] Vars { get { return vars; } }
		#endregion
		
		#region Costructor
        // To be used if rawData is known (DATA from XPlane)
		public XPlane_UDP_Message (byte[] recvData)
		{
			this.rawData = recvData;
			
            DecodeRawInput();
		}

        // To be used if constructing message (DATA to XPlane)
        public XPlane_UDP_Message(string msgType, int index, float[] vars)
        {
            this.msgType = msgType;
            this.index = index;
            this.vars = vars;

            EncodeStructuredInput();
        }
		#endregion
		

		#region Methods

        // Codes a message from structured input
        private void EncodeStructuredInput()
        {
            try 
            {
                rawData = new byte[41];

                // Message type [0-3], always zero [4]
                //System.Buffer.BlockCopy(msgType.ToCharArray(), 0, rawData, 0, 4); //(msgType.Length * sizeof(char)));
                // FIXME fulhack
                rawData[0] = 68; // D
                rawData[1] = 65; // A
                rawData[2] = 84; // T
                rawData[3] = 65; // A

                rawData[4] = 0; 

                // Index [5], always zero [6-8]
                rawData[5] = (byte)this.index;
                rawData[6] = 0;
                rawData[7] = 0;
                rawData[8] = 0;

                // Variables [9-41], each float 4 bytes
                for (int i = 0; i <= 7;i++ )
                {
                    System.Buffer.BlockCopy(BitConverter.GetBytes(vars[i]), 0, rawData, (9+i*4), 4);
                }
            }
            catch (Exception ex)
            {
                throw new System.ArgumentException("CreateMsg(): " + ex.Message);
            }
        }

		// Decodes a message and saves the data in the object
		private void DecodeRawInput()
		{
			try
			{
				int header_offset = 0; // To offset messages without headers 
				
				// If message length is 41 bytes, message has HEADER. bytes: [0-40]
				if(rawData.Length == 41) 
				{
					header_offset = 0; // Header
					
					// -- HEADER -- 5 bytes [0-4]
					// [0-3]: Message type
					this.msgType = Encoding.UTF8.GetString(rawData, 0, 4);
					
					// [4]: Internal var. -- SKIPPED (should be zero for sending)
					
				}
				// If message length is 36 bytes, message doesn't have HEADER. bytes: [0-35]
				else if(rawData.Length == 36)
				{
					header_offset = 5; // No header
					
					// NO HEADER (straight to message)
					this.msgType = "    ";
				}
				// Unknown message type (all other lengths)
				else
				{
					throw new System.ArgumentException("Decode(): Invalid message length", "rawData");
				}
				
				// -- MESSAGE -- 36 bytes [5-40]
				// or no-header: -- MESSAGE -- 36 bytes [0-35]
				// [5]: Index number
				// or no-header: [0]: Index number
				this.index = (int)rawData[5-header_offset];
					
				// [6-8]: Always zero (?) -- SKIPPED
				// or no-header: [1-3]: Always zero (?) -- SKIPPED
				
				// [9-41]: Variables, 32 bytes, up to 8 single-precision floats 
				// or no-header: [4-35]: Variables, 32 bytes, up to 8 single-precision floats
				vars[0] = BitConverter.ToSingle (rawData, 9-header_offset);
				vars[1] = BitConverter.ToSingle (rawData, 13-header_offset);
				vars[2] = BitConverter.ToSingle (rawData, 17-header_offset);
				vars[3] = BitConverter.ToSingle (rawData, 21-header_offset);
				vars[4] = BitConverter.ToSingle (rawData, 25-header_offset);
				vars[5] = BitConverter.ToSingle (rawData, 29-header_offset);
				vars[6] = BitConverter.ToSingle (rawData, 33-header_offset);
				vars[7] = BitConverter.ToSingle (rawData, 37-header_offset);
			}
			catch (Exception ex)
			{
				throw new System.ArgumentException("DecodeRawInput(): " + ex.Message);
			}
		}

		// Returns a string representation of the raw data
		public string RawToString()
		{
			string raw_s = "{";
			for(int i=0;i<rawData.Length;i++)
			{
				if (i != rawData.Length-1)
				{
					raw_s += rawData[i].ToString() + ", ";
				}
				else
				{
					raw_s += rawData[i].ToString() + "}";
				}
			}
			
			return raw_s;
		}
		
		// Returns a string representation of the decoded data
		public string DecodedToString()
		{
			string xmsg_s = "Type [" + this.MsgType + "], Index [" + this.Index.ToString() + "], Vars {";
			for(int i=0;i<Vars.Length;i++)
			{
				if (i != Vars.Length-1)
				{
					xmsg_s += this.Vars[i].ToString() + ", ";
				}
				else
				{
					xmsg_s += this.Vars[i].ToString () + "}";
				}
			}
			
			return xmsg_s;
		}
		
		#endregion
	}
}

